Skip to content

fix(keys/agent): forward full agent signature blob for sk-ecdsa/sk-ed25519 keys - #701

Merged
Eugeny merged 1 commit into
Eugeny:mainfrom
ztbh:main
May 23, 2026
Merged

fix(keys/agent): forward full agent signature blob for sk-ecdsa/sk-ed25519 keys#701
Eugeny merged 1 commit into
Eugeny:mainfrom
ztbh:main

Conversation

@ztbh

@ztbh ztbh commented May 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes authentication failures when using FIDO2/U2F SSH keys
(sk-ecdsa-sha2-nistp256@openssh.com, sk-ssh-ed25519@openssh.com)
through ssh-agent.

Problem

Per OpenSSH's PROTOCOL.u2f, the signature blob for
sk-* key types has the following format:

string  signature_algorithm   // e.g. "sk-ecdsa-sha2-nistp256@openssh.com"
string  inner_signature       // ECDSA (mpint r, mpint s) or Ed25519 raw sig
byte    flags                 // SK_USER_PRESENCE_REQD (0x01),
                              // SK_USER_VERIFICATION_REQD (0x04)
uint32  counter               // FIDO authenticator counter

The trailing flags and counter fields are an integral part of the
signature blob and must be transmitted to the server, which feeds
them back into the FIDO signature verification (they are part of the
authenticator data hashed by the security key).

ssh-key::Signature correctly models this: for sk-* algorithms, the
signature value carries the inner sig + flags + counter together.

However, russh_keys::agent::client::AgentClient::write_signature
only re-encodes the algorithm name (t) and the inner signature
(sig) read from the agent's SSH_AGENT_SIGN_RESPONSE, discarding
the trailing 5 bytes
(1-byte flags + 4-byte counter) when the
signature algorithm is sk-*.

The server then fails verification with:

mm_answer_keyverify: publickey ECDSA-SK signature using
  sk-ecdsa-sha2-nistp256@openssh.com unverified: invalid format

because OpenSSH's ssh_ecdsa_sk_verify() / ssh_ed25519_sk_verify()
hits EOF when reading flags.

Reproduction (before this PR)

  1. Generate a FIDO2 key:
    ssh-keygen -t ecdsa-sk -f ~/.ssh/id_ecdsa_sk
    
  2. Load it into ssh-agent and add the public key to the server's
    ~/.ssh/authorized_keys.
  3. Authenticate via russh against an OpenSSH 8.x/9.x server.
  4. Server rejects with invalid format; client sees
    MSG_USERAUTH_FAILURE.

OpenSSH's own ssh(1) client succeeds against the same server with
the same agent and key, confirming the issue is on the russh side.

Fix

In AgentClient::write_signature, detect sk-* signature algorithms
by checking whether the algorithm name returned by the agent starts
with "sk-". When it does:

  • Reserve an additional 5 bytes in the outer length prefix for the
    trailing flags (1 byte) + counter (4 bytes).
  • After writing the algorithm name and inner signature, decode the
    flags byte and counter u32 from the remaining agent response
    and append them to the output buffer.

For non-sk-* algorithms the behavior is unchanged.

This matches OpenSSH's own behavior, where the agent-produced
signature blob is treated as opaque and forwarded verbatim — see
sshconnect2.c::sign_and_send_pubkey() and ssh-agent.c.

Changes

  • russh-keys/src/agent/client.rs:
    • AgentClient::write_signature now preserves the trailing
      flags + counter for sk-* signatures and adjusts the outer
      length prefix accordingly.

Testing

  • Manual: authentication with sk-ecdsa-sha2-nistp256@openssh.com
    via OpenSSH ssh-agent against OpenSSH 9.x server now succeeds.
  • Manual: authentication with sk-ssh-ed25519@openssh.com also
    succeeds.
  • Regression: non-sk keys (Ed25519, RSA, ECDSA) still authenticate.

@Eugeny
Eugeny merged commit 0e1b6fb into Eugeny:main May 23, 2026
11 checks passed
@Eugeny

Eugeny commented May 23, 2026

Copy link
Copy Markdown
Owner

Thank you! @all-contributors add @ztbh for code

@allcontributors

Copy link
Copy Markdown
Contributor

@Eugeny

I've put up a pull request to add @ztbh! 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants